package com.example.springboot.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.RandomUtil;
import cn.hutool.log.Log;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.example.springboot.common.Constants;
import com.example.springboot.common.ValidationEnum;
import com.example.springboot.controller.dto.UserDTO;
import com.example.springboot.controller.dto.UserPasswordDTO;
import com.example.springboot.entity.Menu;
import com.example.springboot.entity.User;
import com.example.springboot.entity.Validation;
import com.example.springboot.exception.ServiceException;
import com.example.springboot.mapper.RoleMapper;
import com.example.springboot.mapper.RoleMenuMapper;
import com.example.springboot.mapper.UserMapper;
import com.example.springboot.service.IMenuService;
import com.example.springboot.service.IUserService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.example.springboot.service.IValidationService;
import com.example.springboot.utils.TokenUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.mail.SimpleMailMessage;
import org.springframework.mail.javamail.JavaMailSender;
import org.springframework.mail.javamail.MimeMessageHelper;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import javax.mail.MessagingException;
import javax.mail.internet.MimeMessage;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;


/**
 * <p>
 * 服务实现类
 * </p>
 *
 * @author wgc
 * @since 2022-12-20
 */
@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements IUserService {

    @Autowired
    JavaMailSender javaMailSender;

    @Value("${spring.mail.username}")
    private String from;
    //抛出异常日志
    private static final Log LOG = Log.get();

    @Resource
    private RoleMapper roleMapper;

    @Resource
    private RoleMenuMapper roleMenuMapper;

    @Resource
    private IMenuService menuService;

    @Resource
    private UserMapper userMapper;

    @Resource
    private IValidationService validationService;

    @Override
    public UserDTO login(UserDTO userDto) {
        User one = getUserInfo(userDto);
        if (one != null) {
            //复制属性
            BeanUtil.copyProperties(one, userDto, true); //true忽略大小写
            //设置token
            String token = TokenUtils.genToken(one.getId().toString(), one.getPassword());
            userDto.setToken(token);

            String role = one.getRole();  //ROLE_ADMIN

            // 设置用户菜单列表， 实现动态路由
            List<Menu> roleMenus = getRoleMenus(role);

            userDto.setMenus(roleMenus);

            return userDto;
        } else {
            throw new ServiceException(Constants.CODE_600, "用户名或密码错误");
        }

    }

    //注册
    @Override
    public User register(UserDTO userDTO) {
        User one = getUserInfo(userDTO);
        if (one == null) {
            one = new User();
            BeanUtil.copyProperties(userDTO, one, true); //true忽略大小写
            save(one);  //把copy完后的用户对象存储到数据库里面
        } else {
            throw new ServiceException(Constants.CODE_600, "用户已存在");
        }
        return one;
    }

    @Override
    public Page<User> findPage(Page<User> userPage, String username, String email, String address) {
        return userMapper.findPage(userPage, username, email, address);
    }

    // 修改密码
    @Override
    public void updatePassword(UserPasswordDTO userPasswordDTO) {
        int update = userMapper.updatePassword(userPasswordDTO);
        if (update < 1) {
            throw new ServiceException(Constants.CODE_600, "密码错误");
        }
    }

    @Override
    public UserDTO loginEmail(UserDTO userDTO) {
        String email = userDTO.getEmail();
        String code = userDTO.getCode();

        // 先查询邮箱验证的表，看看之前有没有发送过邮箱code, 如果不存在，就重新获取
        QueryWrapper<Validation> validationQueryWrapper = new QueryWrapper<>();
        validationQueryWrapper.eq("email", email);
//        大于等于当前时间
        validationQueryWrapper.ge("time", new Date());
        Validation one = validationService.getOne(validationQueryWrapper);
        if (one == null) {
            throw new ServiceException("-1", "验证码过期，请重新获取");
        }
        validationQueryWrapper.eq("code", code);
        Validation getCode = validationService.getOne(validationQueryWrapper);
        if (getCode == null) {
            throw new ServiceException("-1", "验证码错误");
        }
        // 如果验证通过了就查询用户的信息
        QueryWrapper<User> userQueryWrapper = new QueryWrapper<>();
        userQueryWrapper.eq("email", email); //存根据邮箱email查询用户信息
        User user = getOne(userQueryWrapper);
        if (user == null) {
            throw new ServiceException("-1", "未找到用户");
        }
        //从user复制属性到userDTO
        BeanUtil.copyProperties(user, userDTO, true); //true忽略大小写
        //设置token
        String token = TokenUtils.genToken(user.getId().toString(), user.getPassword());
        userDTO.setToken(token);

        String role = user.getRole();  //ROLE_ADMIN

        // 设置用户菜单列表， 实现动态路由
        List<Menu> roleMenus = getRoleMenus(role);

        userDTO.setMenus(roleMenus);

        return userDTO;
    }

    @Override
    public void sendEmailCode(String email, Integer type) throws MessagingException {
        Date now = new Date();

        // 先查询同类型的code
        QueryWrapper<Validation> validationQueryWrapper = new QueryWrapper<>();
        validationQueryWrapper.eq("email", email);
        validationQueryWrapper.eq("type", ValidationEnum.LOGIN.getCode());
        // 判断有没有过期 lt 小于 ge 是 mm>
        validationQueryWrapper.ge("time", now);  // 查询数据库没过期的code
        Validation validation = validationService.getOne(validationQueryWrapper);
        if (validation != null) {
            throw new ServiceException("-1", "当前您的验证码仍然有效，请不要重复发送");
        }

        String code = RandomUtil.randomNumbers(4); //随机一个4位长度验证码

        if (ValidationEnum.LOGIN.getCode().equals(type)) {
            SimpleMailMessage message = new SimpleMailMessage();
//            message.setCc("抄送人");
//            message.setBcc("密送人");
            message.setFrom(from);
            message.setTo(email);
            message.setSentDate(now);
            message.setSubject("[聪哥科技] 登录邮箱认证");
            message.setText("本次登录的验证码是: " + code + "  有效期5分钟, 请妥善保管, 切勿泄露");
            javaMailSender.send(message);
        } else if (ValidationEnum.FORGET_PASS.getCode().equals(type)) {

            MimeMessage mimeMessage = javaMailSender.createMimeMessage();
            MimeMessageHelper mimeMessageHelper = new MimeMessageHelper(mimeMessage);
            mimeMessageHelper.setSubject("[聪哥科技] 忘记密码邮箱认证");
            mimeMessageHelper.setFrom(from);
            mimeMessageHelper.setTo(email);
            mimeMessageHelper.setSentDate(now);
            // 富文本
            String context = "<b>尊敬的用户: </b><br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;您好，您本次忘记密码的验证码是: " +
                    "<b color=\"red\">"
                    + code + "</b>,<br>" + "有效期5分钟。请妥善保管，切勿泄露";
            mimeMessageHelper.setText(context, true);
            javaMailSender.send(mimeMessage);
        }

        // 发送成功之后,把验证码存到数据库
//        DateUtil.offsetMinute(now,5) 当前时间往后推5分钟
        validationService.saveCode(email, code, type, DateUtil.offsetMinute(now, 5));
    }

    private User getUserInfo(UserDTO userDTO) {
        //到数据库查询泛型对象必须是实体类User
        QueryWrapper<User> queryWrapper = new QueryWrapper<>();

        queryWrapper.eq("username", userDTO.getUsername());
        queryWrapper.eq("password", userDTO.getPassword());

//        List<User> list = list(queryWrapper); //数据库有相同数据可以直接登录
//        return list.size() != 0;

        //如果数据库中有相同的username与password就会抛出异常
        User one;
        try {
            one = getOne(queryWrapper); //从数据库查询用户信息
        } catch (Exception e) {
            LOG.error(e);
            throw new ServiceException(Constants.CODE_500, "系统错误");
        }
        return one;
    }

    /**
     * 获取当前角色 的 菜单列表             // 设置用户菜单列表， 实现动态路由
     *
     * @param roleFlag
     * @return
     */
    private List<Menu> getRoleMenus(String roleFlag) {

        /**
         * @Select("select id from sys_role where flag = #{flag}")
         * */
        Integer roleId = roleMapper.selectByFlag(roleFlag);

        /** 查找 roleMenuMapper 的 flag
         当前角色的所有菜单id集合
         @Select("select menu_id from sys_role_menu where role_id = #{roleId}")
         */
        List<Integer> menuIds = roleMenuMapper.selectByRoleId(roleId);

        // 查出系统所有的菜单          findMenus 设置 menu 的 子菜单
        List<Menu> menus = menuService.findMenus("");
        //new 一个最后筛选完成之后的list
        List<Menu> roleMenus = new ArrayList<>();

        // 再筛选当前用户角色的菜单， 遍历menus
        for (Menu menu : menus) {
            //如果 menuIds 包含 menu里的id
            if (menuIds.contains(menu.getId())) {
                // 就给roleMenu添加
                roleMenus.add(menu);
            }
            List<Menu> children = menu.getChildren();
            // 把children 不包含的菜单id给移除掉
            // removeIf() 移除 children 里面不在 menuIds集合中的 元素
            children.removeIf(child -> !menuIds.contains(child.getId()));

        }
        return roleMenus;
    }
}
